/**
 * 文件说明:初步采用查询的方式进行数据处理
 * 下一步准备测试发送，封装为库，对于飞镖单包通信可以采用定时的方式进行同步，和短时间阻塞
 *
 */
#include <iostream>
#include <stdio.h> 
#include <errno.h>
#include <string.h>
/*
  unistd.h 是 Linux 和 Unix 系统中的一个关键头文件，
  它提供了许多与系统调用和底层操作有关的函数、常量和类型的声明。
  一些常见的函数和常量:
*/
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
//使用 select监视串口实现非阻塞，减少read开销
#include "sys/select.h"

// #include <queue>
//#include <array>

void configureSerialPort(int fd, int baudRate, int dataBits, int stopBits, char parity) {
    struct termios options;
    
    /*
        struct termios
        {
          unsigned short c_iflag;      //输入模式标志
          unsigned short c_oflag;      //输出模式标志
          unsigned short c_cflag;      //控制模式标志
          unsigned short c_lflag;      //本地模式标志
          unsigned char c_line;        //线路规则
          unsigned char c_cc[NCC];     //控制字
        };
        c_iflag字段:IGNPAR 忽略帧错误和奇偶校验错误 INPCK
    */

    // 获取当前串口的设置
    if (tcgetattr(fd, &options) != 0) {
        std::cerr << "Error getting serial port attributes" << std::endl;
        return;
    }
    //不忽略换行和回车
    //options.c_iflag &=~INLCR;
    //options.c_iflag &=~IGNCR;
    //options.c_iflag &=~ICRNL;
    options.c_iflag &= ~(ICRNL | INLCR | IGNCR);//ICRNL 映射输入上的 CR 到 NL;INLCR 将 NL 映射到输入上的 CR;IGNCR 忽略回车符
    

    // 设置波特率
    cfsetispeed(&options, baudRate);
    cfsetospeed(&options, baudRate);

    // 设置数据位（通常为 8 位）
    options.c_cflag &= ~CSIZE;  // 清除数据位设置
    switch (dataBits) {
        case 5: options.c_cflag |= CS5; break;
        case 6: options.c_cflag |= CS6; break;
        case 7: options.c_cflag |= CS7; break;
        case 8: options.c_cflag |= CS8; break;
        default:
            std::cerr << "Invalid data bits" << std::endl;
            return;
    }

    // 设置停止位
    if (stopBits == 1) {
        options.c_cflag &= ~CSTOPB;  // 1 位停止位
    } else if (stopBits == 2) {
        options.c_cflag |= CSTOPB;   // 2 位停止位
    } else {
        std::cerr << "Invalid stop bits" << std::endl;
        return;
    }

    // 设置校验位
    if (parity == 'N') {
        options.c_cflag &= ~PARENB;  // 无校验
        options.c_iflag &= ~INPCK;   // 不启用输入校验
    } else if (parity == 'O') {
        options.c_cflag |= PARENB;   // 开启校验
        options.c_cflag |= PARODD;   // 奇校验
        options.c_iflag |= INPCK;    // 启用输入校验
    } else if (parity == 'E') {
        options.c_cflag |= PARENB;   // 开启校验
        options.c_cflag &= ~PARODD;  // 偶校验
        options.c_iflag |= INPCK;    // 启用输入校验
    } else {
        std::cerr << "Invalid parity option" << std::endl;
        return;
    }

    // 使能串口配置并关闭硬件流控制
    options.c_cflag |= (CLOCAL | CREAD);  // 启用接收并忽略调制解调器控制线
    options.c_cflag &= ~CRTSCTS;          // 禁用硬件流控制
    options.c_iflag &= ~(IXON | IXOFF | IXANY);// 禁用软件流控制
    //options.c_iflag |= IXON | IXOFF | IXANY;
    //软件流控制:options.c_iflag |= IXON | IXOFF | IXANY

    // 设置串口为原始模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag &= ~OPOST;

    // 设置数据传输时的字符大小，接收超时等(no blocked模式无意义)
    options.c_cc[VMIN] = 0;    // 1,至少读取 1 字节数据
    options.c_cc[VTIME] = 0;   // 5,超时 0.5 秒

    // 应用配置
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        std::cerr << "Error setting serial port attributes" << std::endl;
        return;
    }
}
int main(int argc, char *argv[])
{
    //printf("Debug:r:%d,n:%d\n",'\r','\n');
    //打开串口
    int serialPort;
    /*
    serialPort = open("/dev/serial0", O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (serialPort == -1) //初始化串口并获取文件描述符保存到fd变量里
    {
     fprintf(stderr, "Unable to open serial device: %s\n", strerror(errno));
     return 1 ;
    }
    */
    
    //初始化串口并获取文件描述符保存到fd变量里
    if ((serialPort = open("/dev/serial0", O_RDWR | O_NOCTTY | O_NDELAY |O_NONBLOCK)) < 0) //允许读写,非终端控制进程,非阻塞
    {
     fprintf(stderr, "Unable to open serial device: %s\n", strerror(errno));
     return 1 ;
    }
 
 
    // 设置串口参数
    configureSerialPort(serialPort, B115200, 8, 1, 'N');



    // 清空输入缓冲区  Line Control 
    tcflush(serialPort, TCIFLUSH);
    tcflush(serialPort, TCOFLUSH);
    tcflush(serialPort, TCOON); //TCOON 重启输出


    //准备好接收
    std::cout << "Ready to Receive" << std::endl;
    //设置缓冲区和大小
    constexpr int bufferSize = 256; // 缓冲区大小
    char buffer[bufferSize];
    int index = 0; // 当前缓冲区索引
    memset(buffer, 0, bufferSize); // 初始化缓冲区
    //std::array<char,bufferSize> buffer;
    
    
    //select 非阻塞设置
    fd_set readfds;
    struct timeval timeout;
    timeout.tv_sec = 0;//设置超时时间
    timeout.tv_usec = 0;

    //测试发送
    const char *message = "Hello, STM32!";
    int tx_n = write(serialPort, message, strlen(message));
    std::cout << "raspi_send:" << message << ",true size:" << tx_n << std::endl;
    
    
    // 查询缓冲区
    char buffer_data[50];
    int receive_flag = 0;
    

    // 读取数据 目前进行轮询 可使用select进行优化
    while (true) {
        
        // 清空文件描述符集合,保证 select() 始终监听 serialPort
        FD_ZERO(&readfds);
        FD_SET(serialPort, &readfds);
        //select
        int activity = select(serialPort + 1, &readfds, nullptr, nullptr, &timeout);
        
        
        
        if (activity < 0) {
          std::cerr << "select error: " << strerror(errno) << std::endl;
          close(serialPort);
          return 1;
        }
        
        
        if (activity == 0) {
            // 超时（没有数据可读）
            std::cout << "No data within 100 msecond" << std::endl;
        } 
        else {
            // 检查串口是否有可读数据
            if(FD_ISSET(serialPort, &readfds)) {
            
              //std::cout << "Debug:read" << std::endl;
              memset(buffer_data,0,sizeof(buffer_data));
              const int bytes_read = read(serialPort, buffer_data, sizeof(buffer_data));
              std::cout << "Debug:read_complete,byte_read:"<< bytes_read << std::endl;
              int bytes_read_temp = bytes_read;
              int num = 100;
              
              //处理数据
              while(bytes_read_temp > 0 && num > 0) {
                  //std::cout << "Debug:data" << std::endl;
                  //std::cout<< "Debug:current_byte:" <<bytes_read_temp << std::endl;
                  char data = buffer_data[bytes_read - bytes_read_temp]; // 读取一个字符
                  //.std::cout << "Debug:ASCII:"<<(int)data << std::endl;
                  // 如果接收到换行符（假设STM32以换行符结束数据）
                  if(data == '\r')
                  {
                    //std::cout << "r:"<<(int)data << std::endl;
                    receive_flag = 1;
                    bytes_read_temp = bytes_read_temp - 1;
                    num--;
                    continue;
                  }
                  else if (data == '\n' && receive_flag == 1) {
                      //std::cout << "n:"<<(int)data << std::endl;
                      buffer[index] = '\0'; // 添加字符串结束符
                      std::cout << "receive_data:" << buffer << std::endl;
                      index = 0; // 重置缓冲区索引
                      memset(buffer, 0, bufferSize); // 清空缓冲区
                  } 
                  else{//数据未结束
                      if (index < bufferSize - 1) { // 防止缓冲区溢出
                          buffer[index++] = data; // 存储数据到缓冲区
                      } 
                      else {
                          std::cerr << "buff full:" << buffer << std::endl;
                          index = 0; // 重置缓冲区索引
                          memset(buffer, 0, bufferSize); // 清空缓冲区
                          //测试发送
                          //const char *message = "Hello, STM32!";
                          //write(serialPort, message, strlen(message));
                          //std::cout << "raspi_send" << buffer << std::endl;
                      }
                  }
                  
                  receive_flag  = 0;
                  bytes_read_temp = bytes_read_temp - 1;
                  num--;
              }
              if(num == 0)
                std::cout << "num未知错误" << std::endl;
              if(bytes_read_temp != 0)
                std::cout << "已提前检测到结束" << std::endl;
                
              //测试发送
              const char *message = "Hello, STM32!";
              tx_n = write(serialPort, message, strlen(message));
              std::cout << "raspi_send:" << message << ",true size:" << tx_n << std::endl;    
              
            }
        }
        //
        
        usleep(100000); // 延迟100ms，避免CPU占用过高
    }

    // 关闭串口
    close(serialPort);
 
    return 0;
}
